{SEND-time text translation}
(*
const
  BufSize = 16384;

function FormatToBBW(src:PWideChar):PWideChar;
var
  buf:array [0..32] of WideChar;
  p:PWideChar;
  i,j:integer;
begin
  result:=src;
  StrReplaceW(src,'{b}'  ,'[b]');
  StrReplaceW(src,'{/b}' ,'[/b]');
  StrReplaceW(src,'{u}'  ,'[u]');
  StrReplaceW(src,'{/u}' ,'[/u]');
  StrReplaceW(src,'{i}'  ,'[i]');
  StrReplaceW(src,'{/i}' ,'[/i]');
  StrReplaceW(src,'{/cf}','[/color]');
  StrReplaceW(src,'{/bg}','');
  lstrcpyw(buf,'[color=');
  repeat
    i:=Pos('{cf',src);
    if i=0 then break;
    j:=i;
    dec(i);
    while (src[j]<>#0) and (src[j]<>'}') do inc(j);
    if src[j]='}' then inc(j);
    case StrToInt(src+i+3) of
      4,10:  p:='green]';
      5,6:   p:='red]';
      7,14:  p:='magenta]';
      3,11,
      12,13: p:='blue]';
      8,9:   p:='yellow]';
      2,15:  p:='black]';
    else
      {1,16:}  p:='white]';
    end;
    lstrcpyw(buf+7,p);
    lstrcpyw(src+i,src+j);
    StrInsertW(buf,src,i);
  until false;
  repeat
    i:=Pos('{bg',src);
    if i=0 then break;
    j:=i;
    dec(i);
    while (src[j]<>#0) and (src[j]<>'}') do inc(j);
    if src[j]='}' then inc(j);
    lstrcpyw(src+i,src+j);
  until false;
end;

function SendMessageProcW(wParam:WPARAM; lParam:LPARAM):integer; cdecl;
var
  ccs:PCCSDATA;
  uns,s,ss:pWideChar;
  p:PChar;
  present:boolean;
  i:integer;
begin
  if DisablePlugin<>dsPermanent then
  begin
    ccs:=PCCSDATA(lParam);
    if ccs^.wParam=0 then
      present:=Pos('%music%',PChar(ccs^.lParam))<>0
    else // not needed?
    begin
      uns:=PWideChar(ccs^.lParam+lstrlen(PChar(ccs^.lParam))+1);
      present:=Pos('%music%',uns)>0;
    end;

    if present then
    begin
      if CallService(MS_WAT_GETMUSICINFO,0,0)=WAT_PLS_NOTFOUND then
        s:=nil
      else
      begin
        if SimpleMode<>BST_UNCHECKED then
          i:=0
        else
          i:=PluginLink^.CallService(MS_PROTO_GETCONTACTBASEPROTO,ccs^.hContact,0);
        s:=GetMacros(TM_MESSAGE,i);
      end;
  //    if s<>nil then // for empty strings
      begin
        mGetMem(ss,BufSize*SizeOf(pWideChar));
        FillChar(ss^,BufSize*SizeOf(pWideChar),0);
        if ccs^.wParam=0 then
          ANSIToWide(PChar(ccs^.lParam),uns,UserCP);
        lstrcpyw(ss,uns);
        if ccs^.wParam=0 then
          mFreeMem(uns);
        StrReplaceW(ss,'%music%',s);
        mFreeMem(s);
        if Pos('{',ss)<>0 then
          FormatToBBW(ss);
        s:=PWideChar(ccs^.lParam);
        WideToANSI(ss,p,UserCP);
        if ccs^.wParam=0 then
        begin
          ccs^.lParam:=dword(p);
        end
        else
        begin
          move(PChar(ss)^,(PChar(ss)+lstrlen(p)+1)^,
            (lstrlenw(ss)+1)*SizeOf(WideChar));
          lstrcpy(PChar(ss),p);
          ccs^.lParam:=dword(ss);
        end;
        result:=CallService(MS_PROTO_CHAINSEND,wParam,lParam);
        mFreeMem(p);
        ccs^.lParam:=dword(s);
        mFreeMem(ss);
        exit;
      end;
    end;
  end;
  result:=CallService(MS_PROTO_CHAINSEND,wParam,lParam);
end;
*)
procedure AddEvent(hContact:THANDLE;atype,flag:integer;data:pointer;size:integer;time:dword=0);
var
  dbeo:TDBEVENTINFO;
begin
  FillChar(dbeo,SizeOf(dbeo),0);
  with dbeo do
  begin
    cbSize   :=SizeOf(dbeo);
    eventType:=atype;
    szModule :=PluginShort;
if data=nil then
begin
  PChar(data):='';
  size:=1;
end;
    pBlob    :=data;
    cbBlob   :=size;
    flags    :=flag;
    if time<>0 then
      timestamp:=time
    else
      timestamp:=GetCurrentTime;
  end;
  PluginLink^.CallService(MS_DB_EVENT_ADD,hContact,dword(@dbeo));
end;

function ReceiveMessageProcW(wParam:WPARAM; lParam:LPARAM):integer; cdecl;
const
  bufsize = 4096*SizeOf(WideChar);
var
  ccs:PCCSDATA;
  s:pWideChar;
  buf:PWideChar;
  base64:TNETLIBBASE64;
//  pos_artist,pos_title,pos_album:PwideChar;
  pos_template:pWideChar;
  curpos:pWideChar;
  encbuf:pWideChar;
  i:integer;
  textpos:PWideChar;
  pc:PChar;
  isNewRequest:bool;
begin
  ccs:=PCCSDATA(lParam);
  result:=0;
  mGetMem(buf,bufsize);
  if strcmpn(PPROTORECVEVENT(ccs^.lParam)^.szMessage,
     wpRequestNew,Length(wpRequestNew))=0 then
    isNewRequest:=true
  else
    isNewRequest:=false;
  if isNewRequest or 
     (strcmpn(PPROTORECVEVENT(ccs^.lParam)^.szMessage,
             wpRequest,Length(wpRequest))=0) then
  begin
    lstrcpy(PChar(buf),PChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,ccs^.hContact,0)));
    i:=DBReadWord(ccs^.hContact,PChar(buf),'ApparentMode');
    lstrcat(PChar(buf),PS_GETSTATUS);
    if (i=ID_STATUS_OFFLINE) or
       ((i=0) and (CallService(PChar(buf),0,0)=ID_STATUS_INVISIBLE)) then
    begin
      result:=CallService(MS_PROTO_CHAINRECV,wParam,lParam);
    end
    else if DBReadByte(ccs^.hContact,strCList,ShareOptText,0)<>0 then
// or (NotListedAllow and (DBReadByte(ccs^.hContact,strCList,'NotOnList',0))
    begin
      if (HistMask and hmInRequest)<>0 then
        AddEvent(ccs^.hContact,EVENTTYPE_WAT_REQUEST,DBEF_READ,nil,0,
                PPROTORECVEVENT(ccs^.lParam)^.timestamp);
      if GetContactStatus(ccs^.hContact)<>ID_STATUS_OFFLINE then
      begin
//!! Request Answer
        curpos:=nil;
        if DisablePlugin<>dsPermanent then
        begin
          if CallService(MS_WAT_GETMUSICINFO,0,0)=WAT_PLS_NOTFOUND then
          begin
            s:=#0#0#0'No player found at this time';
            textpos:=s+3;
          end
          else
          begin
            if not isNewRequest then
            begin
              FillChar(buf^,bufsize,0);
              lstrcpyw(buf,SongInfo.artist);   curpos:=strendw(buf)+1;
              lstrcpyw(curpos,SongInfo.title); curpos:=strendw(curpos)+1;
              lstrcpyw(curpos,SongInfo.album); curpos:=strendw(curpos)+1;
            end
            else
              curpos:=buf;
//!! check to DisableTemporary
            if SimpleMode<>BST_UNCHECKED then
              i:=0
            else
              i:=PluginLink^.CallService(MS_PROTO_GETCONTACTBASEPROTO,ccs^.hContact,0);
            s:=GetMacros(TM_MESSAGE,i);
            textpos:=lstrcpyw(curpos,s);
            mFreeMem(s);
            curpos:=strendw(curpos)+1;
          end;
        end
        else
        begin
          s:=#0#0#0'Sorry, but i don''t use WATrack right now!';
          textpos:=s+3;
        end;
// encode
        if not isNewRequest then
        begin
          if curpos<>nil then
          begin
            base64.pbDecoded:=PByte(buf);
            base64.cbDecoded:=PChar(curpos)-PChar(buf);
          end
          else
          begin
            base64.pbDecoded:=PByte(s);
            base64.cbDecoded:=(lstrlenw(textpos)+3+1)*SizeOf(PWideChar);
          end;
          base64.cchEncoded:=Netlib_GetBase64EncodedBufferSize(base64.cbDecoded);
          mGetMem(encbuf,base64.cchEncoded+1+Length(wpAnswer));
          base64.pszEncoded:=PChar(encbuf)+Length(wpAnswer);
          lstrcpy(PChar(encbuf),wpAnswer);
          PluginLink^.CallService(MS_NETLIB_BASE64ENCODE,0,dword(@base64));
          if (HistMask and hmOutInfo)<>0 then
            AddEvent(ccs^.hContact,EVENTTYPE_WAT_ANSWER,DBEF_SENT,
                     base64.pbDecoded,base64.cbDecoded);
          CallContactService(ccs^.hContact,PSS_MESSAGE,0,dword(encbuf));
        end
        else
        begin
          i:=WideToCombo(textpos,encbuf,UserCP);
          if (HistMask and hmOutInfo)<>0 then
            AddEvent(ccs^.hContact,EVENTTYPE_WAT_MESSAGE,DBEF_SENT,encbuf,i);
//          if CallContactService(ccs^.hContact,PSS_MESSAGEW,PREF_UNICODE,dword(encbuf))=
//             ACKRESULT_FAILED then
            CallContactService(ccs^.hContact,PSS_MESSAGE,PREF_UNICODE,dword(encbuf));
        end;
        mFreeMem(encbuf);
      end;
    end
    else
    begin
      if (HistMask and hmIRequest)<>0 then
        AddEvent(ccs^.hContact,EVENTTYPE_WAT_REQUEST,DBEF_READ,nil,0,
                 PPROTORECVEVENT(ccs^.lParam)^.timestamp);
      if (HistMask and hmISend)<>0 then
      begin
//!! Request Error Answer
        if isNewRequest then
          pc:=PChar(buf)
        else
        begin
          lstrcpy(PChar(buf),wpError);
          pc:=PChar(buf)+Length(wpError);
        end;
        lstrcpy(pc,'Sorry, but you have no permission to obtain this info!');
        CallContactService(ccs^.hContact,PSS_MESSAGE,0,dword(buf));
        if (HistMask and hmOutError)<>0 then
        begin
          AddEvent(ccs^.hContact,EVENTTYPE_WAT_ERROR,DBEF_SENT,nil,0,
                   PPROTORECVEVENT(ccs^.lParam)^.timestamp);
        end;
      end;
    end;
  end
  else if strcmpn(PPROTORECVEVENT(ccs^.lParam)^.szMessage,wpAnswer,Length(wpAnswer))=0 then
  begin
// decode
    base64.pszEncoded:=PPROTORECVEVENT(ccs^.lParam)^.szMessage+Length(wpAnswer);
    base64.cchEncoded:=lstrlen(base64.pszEncoded);
    base64.cbDecoded :=Netlib_GetBase64DecodedBufferSize(base64.cchEncoded);
    mGetMem(base64.pbDecoded,base64.cbDecoded);

    PluginLink^.CallService(MS_NETLIB_BASE64DECODE,0,dword(@base64));

    curpos:=pWideChar(base64.pbDecoded);           // pos_artist:=curpos;
    while curpos^<>#0 do inc(curpos); inc(curpos); // pos_title :=curpos;
    while curpos^<>#0 do inc(curpos); inc(curpos); // pos_album :=curpos;
    while curpos^<>#0 do inc(curpos); inc(curpos);
    pos_template:=curpos;

    if (HistMask and hmInInfo)<>0 then
      AddEvent(ccs^.hContact,EVENTTYPE_WAT_ANSWER,DBEF_READ,
          base64.pbDecoded,base64.cbDecoded,
          PPROTORECVEVENT(ccs^.lParam)^.timestamp);
//  Action

    lstrcpyw(buf,TranslateW('Music Info from '));
    lstrcatw(buf,PWideChar(CallService(MS_CLIST_GETCONTACTDISPLAYNAME,ccs^.hContact,GCDNF_UNICODE)));

    MessageBoxW(0,TranslateW(pos_template),buf,MB_ICONINFORMATION);

    mFreeMem(base64.pbDecoded);
  end
  else if strcmpn(PPROTORECVEVENT(ccs^.lParam)^.szMessage,wpError,Length(wpError))=0 then
  begin
    if (HistMask and hmInError)<>0 then
      AddEvent(ccs^.hContact,EVENTTYPE_WAT_ERROR,DBEF_READ,nil,0,
               PPROTORECVEVENT(ccs^.lParam)^.timestamp);
{
    AnsiToWide(PChar(CallService(MS_CLIST_GETCONTACTDISPLAYNAME,ccs^.hContact,0)),s);
    lstrcpyw(buf,s);
    lstrcatw(buf,TranslateW(' answer you'));
    mFreeMem(s);
}
    MessageBox(0,Translate(PPROTORECVEVENT(ccs^.lParam)^.szMessage+Length(wpError)),
               Translate('You Get Error'),MB_ICONERROR);
  end
  else
    result:=CallService(MS_PROTO_CHAINRECV,wParam,lParam);
  mFreeMem(buf);
end;

function SendRequest(hContact:WPARAM;lParam:LPARAM):integer; cdecl;
var
  buf:array [0..2047] of char;
begin
  result:=0;
  lstrcpy(buf,wpRequest);
  lstrcpy(buf+Length(wpRequest),SendRequestText);
  CallContactService(hContact,PSS_MESSAGE,0,dword(@buf));
  if (HistMask and hmOutRequest)<>0 then
    AddEvent(hContact,EVENTTYPE_WAT_REQUEST,DBEF_SENT,nil,0);
end;

procedure SetProtocol;
var
  desc:TPROTOCOLDESCRIPTOR;
begin
  desc.cbSize:=PROTOCOLDESCRIPTOR_V3_SIZE;//SizeOf(desc);
  desc.szName:=PluginShort;
  desc._type :=PROTOTYPE_TRANSLATION;

  CallService(MS_PROTO_REGISTERMODULE,0,dword(@desc));
//  CreateProtoServiceFunction(PluginShort,PSS_MESSAGE ,@SendMessageProcW);
//  CreateProtoServiceFunction(PluginShort,PSS_MESSAGEW,@SendMessageProcW);
  hSRM:=CreateProtoServiceFunction(PluginShort,PSR_MESSAGE ,@ReceiveMessageProcW);
//  CreateProtoServiceFunction(PluginShort,PSR_MESSAGEW,@ReceiveMessageProcW);

  hGCI:=PluginLink^.CreateServiceFunction(MS_WAT_GETCONTACTINFO,@SendRequest);
  contexthook:=PluginLink^.HookEvent(ME_CLIST_PREBUILDCONTACTMENU,@OnContactMenu);
end;

procedure ClearProtocol;
begin
  PluginLink^.UnhookEvent(contexthook);
  PluginLink^.UnhookEvent(hAddUserHook);
end;

procedure RegisterContacts;
var
  hContact:integer;
begin
  hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
  while hContact<>0 do
  begin
    CallService(MS_PROTO_ADDTOCONTACT,hContact,dword(PluginShort));
    hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
  end;
end;

function HookAddUser(hContact:WPARAM;lParam:LPARAM):integer; cdecl;
begin
  result:=0;
  CallService(MS_PROTO_ADDTOCONTACT,hContact,dword(PluginShort));
end;
